{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "view-in-github"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/CoreTheGreat/HBPU-Machine-Learning-Course/blob/main/ML_Chapter3_Classification.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lPboLx_o0UxI"
   },
   "source": [
    "# 第五章：深度学习\n",
    "湖北理工学院《机器学习》课程资料\n",
    "\n",
    "作者：李辉楚吴\n",
    "\n",
    "笔记内容概述: 前馈神经网络、全连接网络、手写字母识别"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 按照Classification章节，读取MNIST数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 1000
    },
    "id": "sM-ziKb94S9_",
    "outputId": "9a8bd59b-1ed4-47e3-e118-cee1849a610a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test set size: 10000\n",
      "Training set size: 60000\n",
      "Number of training samples: 43360\n",
      "Number of cross-validation samples: 10850\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torchvision\n",
    "import torchvision.transforms as transforms\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "label_size = 18 # Label size\n",
    "ticklabel_size = 14 # Tick label size\n",
    "\n",
    "class FlattenTransform:\n",
    "    def __call__(self, tensor):\n",
    "        ''' \n",
    "        Flatten tensor into an 1-D vector\n",
    "        '''\n",
    "        return tensor.view(-1)\n",
    "    \n",
    "# Define a transform to normalize the data\n",
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    FlattenTransform()\n",
    "])\n",
    "\n",
    "# Load test data from the MNIST\n",
    "testset = torchvision.datasets.MNIST(root='./Data', train=False, download=False, transform=transform)\n",
    "print(f\"Test set size: {len(testset)}\")\n",
    "\n",
    "# Load training data from the MNIST\n",
    "trainset = torchvision.datasets.MNIST(root='./Data', train=True, download=False, transform=transform)\n",
    "print(f\"Training set size: {len(trainset)}\")\n",
    "\n",
    "# Rate of trX and cvX\n",
    "tr_cv_rate = 0.8\n",
    "\n",
    "# Create a list to store indices for each class\n",
    "class_indices = [[] for _ in range(10)]  # 10 classes in MNIST\n",
    "\n",
    "# Populate class_indices\n",
    "for idx, (_, label) in enumerate(trainset):\n",
    "    class_indices[label].append(idx)\n",
    "\n",
    "# Calculate the number of samples for each class in training and validation sets\n",
    "train_size_per_class = int(tr_cv_rate * min(len(indices) for indices in class_indices))\n",
    "val_size_per_class = min(len(indices) for indices in class_indices) - train_size_per_class\n",
    "\n",
    "# Create balanced train and validation sets\n",
    "train_indices = []\n",
    "val_indices = []\n",
    "for indices in class_indices:\n",
    "    train_indices.extend(indices[:train_size_per_class])\n",
    "    val_indices.extend(indices[train_size_per_class:train_size_per_class + val_size_per_class])\n",
    "\n",
    "# Create Subset datasets\n",
    "from torch.utils.data import Subset\n",
    "trX = Subset(trainset, train_indices)\n",
    "cvX = Subset(trainset, val_indices)\n",
    "\n",
    "print(f\"Number of training samples: {len(trX)}\")\n",
    "print(f\"Number of cross-validation samples: {len(cvX)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "构建DataLoaders，准备训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "BWPSDyOq5qOO",
    "outputId": "9aed06f2-67a9-4a6f-f00c-d91554c3a260",
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input_size is 784\n",
      "tensor([[0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n",
      "        [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],\n",
      "        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n",
      "        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])\n"
     ]
    }
   ],
   "source": [
    "batch_size = 42 # Define training batch\n",
    "\n",
    "def one_hot_collate(batch):\n",
    "    data = torch.stack([item[0] for item in batch])\n",
    "    labels = torch.tensor([item[1] for item in batch])\n",
    "    one_hot_labels = torch.zeros(labels.size(0), 10)  # 10 classes in MNIST\n",
    "    one_hot_labels.scatter_(1, labels.unsqueeze(1), 1)\n",
    "    return data, one_hot_labels\n",
    "\n",
    "trLoader = torch.utils.data.DataLoader(trX, batch_size=batch_size, shuffle=True, num_workers=0, collate_fn=one_hot_collate)\n",
    "cvLoader = torch.utils.data.DataLoader(cvX, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)\n",
    "teLoader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate)\n",
    "\n",
    "# Get a batch of training data\n",
    "dataiter = iter(trLoader)\n",
    "data, labels = next(dataiter)\n",
    "\n",
    "input_size = data[0].numpy().shape[0]\n",
    "print(f'Input_size is {input_size}')\n",
    "print(labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义并训练全连接神经网络\n",
    "* 输入：1-D向量\n",
    "* 输出：手写字母类型的概率分布\n",
    "* 隐藏层：2层\n",
    "* 节点数：100个/层"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FNN(\n",
      "  (fc1): Linear(in_features=784, out_features=10, bias=True)\n",
      "  (relu1): ReLU()\n",
      "  (fc2): Linear(in_features=10, out_features=10, bias=True)\n",
      "  (relu2): ReLU()\n",
      "  (fc3): Linear(in_features=10, out_features=10, bias=True)\n",
      "  (softmax): Softmax(dim=1)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "import torch.nn as nn\n",
    "\n",
    "class FNN(nn.Module):\n",
    "    def __init__(self, input_size, hidden_size, num_classes):\n",
    "        super(FNN, self).__init__()\n",
    "        self.fc1 = nn.Linear(input_size, hidden_size)\n",
    "        self.relu1 = nn.ReLU()\n",
    "        self.fc2 = nn.Linear(hidden_size, hidden_size)\n",
    "        self.relu2 = nn.ReLU()\n",
    "        self.fc3 = nn.Linear(hidden_size, num_classes)\n",
    "        self.softmax = nn.Softmax(dim=1)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        x = self.fc1(x)\n",
    "        x = self.relu1(x)\n",
    "        x = self.fc2(x)\n",
    "        x = self.relu2(x)\n",
    "        x = self.fc3(x)\n",
    "        out = self.softmax(x)\n",
    "        return out\n",
    "\n",
    "# Define the model parameters\n",
    "hidden_size = 10\n",
    "num_classes = 10  # MNIST has 10 classes (digits 0-9)\n",
    "\n",
    "# Instantiate the model\n",
    "model = FNN(input_size, hidden_size, num_classes)\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用Adam作为Optimizor训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch [1/50], Train Loss: 1.8090, CV Loss: 1.6696\n",
      "Epoch [2/50], Train Loss: 1.6559, CV Loss: 1.6536\n",
      "Epoch [3/50], Train Loss: 1.6442, CV Loss: 1.6455\n",
      "Epoch [4/50], Train Loss: 1.6380, CV Loss: 1.6435\n",
      "Epoch [5/50], Train Loss: 1.6339, CV Loss: 1.6398\n",
      "Epoch [6/50], Train Loss: 1.6302, CV Loss: 1.6398\n",
      "Epoch [7/50], Train Loss: 1.6279, CV Loss: 1.6371\n",
      "Epoch [8/50], Train Loss: 1.6248, CV Loss: 1.6353\n",
      "Epoch [9/50], Train Loss: 1.6230, CV Loss: 1.6405\n",
      "Epoch [10/50], Train Loss: 1.6212, CV Loss: 1.6354\n",
      "Epoch [11/50], Train Loss: 1.6196, CV Loss: 1.6329\n",
      "Epoch [12/50], Train Loss: 1.6180, CV Loss: 1.6306\n",
      "Epoch [13/50], Train Loss: 1.6166, CV Loss: 1.6314\n",
      "Epoch [14/50], Train Loss: 1.6151, CV Loss: 1.6292\n",
      "Epoch [15/50], Train Loss: 1.6142, CV Loss: 1.6293\n",
      "Epoch [16/50], Train Loss: 1.6132, CV Loss: 1.6282\n",
      "Epoch [17/50], Train Loss: 1.6122, CV Loss: 1.6324\n",
      "Epoch [18/50], Train Loss: 1.6118, CV Loss: 1.6277\n",
      "Epoch [19/50], Train Loss: 1.6109, CV Loss: 1.6263\n",
      "Epoch [20/50], Train Loss: 1.6100, CV Loss: 1.6267\n",
      "Epoch [21/50], Train Loss: 1.6093, CV Loss: 1.6267\n",
      "Epoch [22/50], Train Loss: 1.6084, CV Loss: 1.6241\n",
      "Epoch [23/50], Train Loss: 1.6077, CV Loss: 1.6268\n",
      "Epoch [24/50], Train Loss: 1.6070, CV Loss: 1.6239\n",
      "Epoch [25/50], Train Loss: 1.6067, CV Loss: 1.6238\n",
      "Epoch [26/50], Train Loss: 1.6059, CV Loss: 1.6246\n",
      "Epoch [27/50], Train Loss: 1.6054, CV Loss: 1.6246\n",
      "Epoch [28/50], Train Loss: 1.6047, CV Loss: 1.6243\n",
      "Epoch [29/50], Train Loss: 1.6041, CV Loss: 1.6226\n",
      "Epoch [30/50], Train Loss: 1.6040, CV Loss: 1.6234\n",
      "Epoch [31/50], Train Loss: 1.6039, CV Loss: 1.6218\n",
      "Epoch [32/50], Train Loss: 1.6034, CV Loss: 1.6224\n",
      "Epoch [33/50], Train Loss: 1.6021, CV Loss: 1.6204\n",
      "Epoch [34/50], Train Loss: 1.6025, CV Loss: 1.6234\n",
      "Epoch [35/50], Train Loss: 1.6015, CV Loss: 1.6201\n",
      "Epoch [36/50], Train Loss: 1.6018, CV Loss: 1.6207\n",
      "Epoch [37/50], Train Loss: 1.6010, CV Loss: 1.6204\n",
      "Epoch [38/50], Train Loss: 1.6007, CV Loss: 1.6222\n",
      "Epoch [39/50], Train Loss: 1.6007, CV Loss: 1.6216\n",
      "Epoch [40/50], Train Loss: 1.6005, CV Loss: 1.6207\n",
      "Epoch [41/50], Train Loss: 1.5994, CV Loss: 1.6225\n",
      "Epoch [42/50], Train Loss: 1.5992, CV Loss: 1.6195\n",
      "Epoch [43/50], Train Loss: 1.5986, CV Loss: 1.6205\n",
      "Epoch [44/50], Train Loss: 1.5987, CV Loss: 1.6190\n",
      "Epoch [45/50], Train Loss: 1.5985, CV Loss: 1.6196\n",
      "Epoch [46/50], Train Loss: 1.5977, CV Loss: 1.6199\n",
      "Epoch [47/50], Train Loss: 1.5979, CV Loss: 1.6195\n",
      "Epoch [48/50], Train Loss: 1.5976, CV Loss: 1.6186\n",
      "Epoch [49/50], Train Loss: 1.5972, CV Loss: 1.6188\n",
      "Epoch [50/50], Train Loss: 1.5969, CV Loss: 1.6202\n"
     ]
    }
   ],
   "source": [
    "# Define loss function and optimizer\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "optimizer = torch.optim.Adam(model.parameters())\n",
    "\n",
    "# Lists to store losses\n",
    "train_losses = []\n",
    "cv_losses = []\n",
    "\n",
    "# Number of epochs\n",
    "num_epochs = 50\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    model.train()\n",
    "    batch_losses = []\n",
    "    \n",
    "    for batch_x, batch_y in trLoader:\n",
    "        # Forward pass\n",
    "        outputs = model(batch_x)\n",
    "        loss = criterion(outputs, batch_y)\n",
    "        \n",
    "        # Backward pass and optimize\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        batch_losses.append(loss.item())\n",
    "    \n",
    "    # Calculate average training loss for this epoch\n",
    "    avg_train_loss = sum(batch_losses) / len(batch_losses)\n",
    "    train_losses.append(avg_train_loss)\n",
    "    \n",
    "    # Evaluate on cross-validation set\n",
    "    model.eval()\n",
    "    cv_batch_losses = []\n",
    "    with torch.no_grad():\n",
    "        for cv_x, cv_y in cvLoader:\n",
    "            cv_outputs = model(cv_x)\n",
    "            cv_loss = criterion(cv_outputs, cv_y)\n",
    "            cv_batch_losses.append(cv_loss.item())\n",
    "    \n",
    "    avg_cv_loss = sum(cv_batch_losses) / len(cv_batch_losses)\n",
    "    cv_losses.append(avg_cv_loss)\n",
    "    \n",
    "    print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, CV Loss: {avg_cv_loss:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "计算识别精度，展示学习曲线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on training set: 86.55%\n",
      "Accuracy on cross-validation set: 84.00%\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1cAAAHUCAYAAADWedKvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAB5FklEQVR4nO3deXhTVf7H8U+SNkn3haULUPYd2UQQGFkEBVQERcENQVFHRUfHZRzcwHFBmVHRUXEcF9xBRRh/KjuKC6igFlAR2deWvXubtsn9/XGbtKEFCi1NU96v58mT5N6bm5P0ov30nPM9FsMwDAEAAAAAqsQa6AYAAAAAQF1AuAIAAACAakC4AgAAAIBqQLgCAAAAgGpAuAIAAACAakC4AgAAAIBqQLgCAAAAgGpAuAIAAACAakC4AgAAAIBqQLgCcNqwWCyVun355ZdVep8pU6bIYrGc1Gu//PLLamlDbTd+/Hg1a9asUsd6PB69/fbbGjx4sOrXr6/Q0FA1bNhQF110kf7v//5PHo/n1Da2itasWSOLxaK///3vRz1m48aNslgs+stf/lLp81Z0nQ0YMEADBgw47mu3bdsmi8WimTNnVvr9vH777TdNmTJF27ZtK7fvRH6u1c1isei2224LyHsDgFdIoBsAADVl5cqVfs8fffRRffHFF1q2bJnf9g4dOlTpfW644QYNHTr0pF7bvXt3rVy5ssptqCsKCgo0cuRILVq0SFdccYVmzJihxMRE7d+/XwsWLNDll1+u2bNna8SIEYFu6lF16dJFZ555pt566y09/vjjstls5Y554403JEkTJkyo0nu99NJLVXp9Zfz222965JFHNGDAgHJB6qGHHtIdd9xxytsAALUV4QrAaePss8/2e96gQQNZrdZy24+Ul5en8PDwSr9P48aN1bhx45NqY3R09HHbczq56667tHDhQr355pu69tpr/fZdeumluvfee5Wfn3/U1xcVFclisSgkJLD/u5swYYJuvfVWzZ8/XxdddJHfPrfbrbfeektnnnmmunTpUqX3CXQob9myZUDfHwACjWGBAFDGgAED1KlTJ3311Vfq06ePwsPDdf3110uSZs+erfPPP19JSUkKCwtT+/bt9fe//125ubl+56houFazZs100UUXacGCBerevbvCwsLUrl07vf76637HVTQscPz48YqMjNSmTZt0wQUX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/vvvyzAM3/bt27drxYoVx319YmKirxflaBUGN2/erLVr1x7zPL1791ZYWJjeeecdv+27du3SsmXLNGjQoApf16ZNGz344IM644wz9NNPP5Xbb7PZ1KtXL7344ouS5Dumbdu26tGjh+9WtnpecnKyhg4dqvfff18vvviirFarxo0b59tf3dfZwIEDlZ2drU8++cRv+3vvvef33GKxyDCMcu/76quvyu12+207sofyWLzf7ZHf/Zw5c5Sbm3vU7/5UGDRokHJycjRv3jy/7d5rq6K2OBwO9e/fX0899ZQk6eeffy53TGxsrC677DJNnDhRhw4dqnBxZQCnD3quAKAS+vTpo7i4ON18882aPHmyQkND9e6772rNmjWBbprPuHHj9Oyzz+qaa67RY489platWmn+/PlauHChJB23Ot9FF12kRx99VJMnT1b//v21YcMG/eMf/1Dz5s1VXFx8wu2xWq169NFHdcMNN+iSSy7RjTfeqIyMDE2ZMqVSwwIlc6jili1bNH78eC1cuFCXXHKJEhISdODAAS1evFhvvPGGZs2a5ZvvVZHY2Fg99NBDuv/++3Xttdfqyiuv1MGDB/XII4/I6XRq8uTJkswgedttt+nyyy9X69atZbfbtWzZMq1du9bXm/Pyyy9r2bJluvDCC5WSkqKCggJfqfDBgwdX6jNNmDBBn332ma/MfJMmTXz7qvs6u/baa/Xss8/q2muv1eOPP67WrVvr888/910TXtHR0erXr5/++c9/qn79+mrWrJmWL1+u1157TbGxsX7HdurUSZL0yiuvKCoqSk6nU82bN69wSN95552nIUOG6L777lNWVpb69u3rqxbYrVs3jR079qQ+19Fs3rxZH330UbntHTp00LXXXqsXX3xR48aN07Zt23TGGWfom2++0RNPPKELLrjA9/N7+OGHtWvXLg0aNEiNGzdWRkaGnnvuOb+5b8OHD1enTp3Uo0cPNWjQQNu3b9f06dPVtGlTX+VJAKepwNbTAIDAOVq1wI4dO1Z4/IoVK4zevXsb4eHhRoMGDYwbbrjB+Omnn8pVXTtatcALL7yw3DmPrO52tGqBR7bzaO+zY8cO49JLLzUiIyONqKgoY9SoUcbnn39eYcW4I7lcLuOee+4xGjVqZDidTqN79+7GvHnzylWA81YL/Oc//1nuHJKMyZMn+2179dVXjdatWxt2u91o06aN8frrr5c757EUFxcbb775pnHuueca8fHxRkhIiNGgQQNj2LBhxnvvvWe43W7DMEq/uw8//LDC87z66qtG586dDbvdbsTExBgjRowwfv31V9/+vXv3GuPHjzfatWtnREREGJGRkUbnzp2NZ5991lcVceXKlcYll1xiNG3a1HA4HEa9evWM/v37G5988kmlPothGEZhYaGRkJBQYeU6w6jadXbk9WQYhrFr1y5j1KhRftfEihUryp3Pe1xcXJwRFRVlDB061Pjll1+Mpk2bGuPGjfM75/Tp043mzZsbNpvN7zwV/Vzz8/ON++67z2jatKkRGhpqJCUlGbfccotx+PBhv+Mq+2/kaCQd9ea9Jg8ePGjcfPPNRlJSkhESEmI0bdrUmDRpklFQUOA7z6effmoMGzbMaNSokWG3242GDRsaF1xwgfH111/7jnn66aeNPn36GPXr1zfsdruRkpJiTJgwwdi2bdtx2wmgbrMYRpmxGgCAOueJJ57Qgw8+qB07dqhx48aBbg4AAHUWwwIBoA554YUXJEnt2rVTUVGRli1bpueff17XXHMNwQoAgFOMcAUAdUh4eLieffZZbdu2TS6XSykpKbrvvvv04IMPBrppAADUeQwLBAAAAIBqQCl2AAAAAKgGhCsAAAAAqAaEKwAAAACoBhS0qIDH49GePXsUFRUli8US6OYAAAAACBDDMJSdna3k5GRZrcfumyJcVWDPnj1q0qRJoJsBAAAAoJbYuXPncZc1IVxVICoqSpL5BUZHRwe4NQAAAAACJSsrS02aNPFlhGMhXFXAOxQwOjqacAUAAACgUtOFKGgBAAAAANWAcAUAAAAA1YBwBQAAAADVgDlXAAAApznDMFRcXCy32x3opgABERoaKpvNVuXzEK4AAABOY4WFhUpLS1NeXl6gmwIEjMViUePGjRUZGVml8xCuAAAATlMej0dbt26VzWZTcnKy7HZ7pSqiAXWJYRjav3+/du3apdatW1epB4twBQAAcJoqLCyUx+NRkyZNFB4eHujmAAHToEEDbdu2TUVFRVUKVxS0AAAAOM1ZrfxKiNNbdfXY8i8JAAAAAKoB4QoAAAAAqgHhCgAAAKe9AQMG6M4776z08du2bZPFYlFqauopaxOCD+EKAAAAQcNisRzzNn78+JM678cff6xHH3200sc3adJEaWlp6tSp00m9X2UR4oIL1QIBAAAQNNLS0nyPZ8+erYcfflgbNmzwbQsLC/M7vqioSKGhocc9b3x8/Am1w2azKTEx8YReg7qPnqta7tWvt2jIs1/p1a+3BLopAACgjjMMQ3mFxQG5GYZRqTYmJib6bjExMbJYLL7nBQUFio2N1QcffKABAwbI6XTqnXfe0cGDB3XllVeqcePGCg8P1xlnnKH333/f77xHDgts1qyZnnjiCV1//fWKiopSSkqKXnnlFd/+I3uUvvzyS1ksFi1dulQ9evRQeHi4+vTp4xf8JOmxxx5Tw4YNFRUVpRtuuEF///vf1bVr15P6eUmSy+XSX/7yFzVs2FBOp1N/+tOftGrVKt/+w4cP6+qrr1aDBg0UFham1q1b64033pBkluK/7bbblJSUJKfTqWbNmmnq1Kkn3RbQc1XrHcot1Ia92dqdkR/opgAAgDouv8itDg8vDMh7//aPIQq3V8+vpvfdd5+efvppvfHGG3I4HCooKNCZZ56p++67T9HR0frss880duxYtWjRQr169TrqeZ5++mk9+uijuv/++/XRRx/plltuUb9+/dSuXbujvuaBBx7Q008/rQYNGujmm2/W9ddfr2+//VaS9O677+rxxx/XSy+9pL59+2rWrFl6+umn1bx585P+rH/72980Z84cvfnmm2ratKmmTZumIUOGaNOmTYqPj9dDDz2k3377TfPnz1f9+vW1adMm5eebv1c+//zz+uSTT/TBBx8oJSVFO3fu1M6dO0+6LSBc1XqRTvNHlFNQHOCWAAAABIc777xTl156qd+2e+65x/f49ttv14IFC/Thhx8eM1xdcMEFuvXWWyWZge3ZZ5/Vl19+ecxw9fjjj6t///6SpL///e+68MILVVBQIKfTqX//+9+aMGGCrrvuOknSww8/rEWLFiknJ+ekPmdubq5mzJihmTNnatiwYZKk//73v1q8eLFee+013XvvvdqxY4e6deumHj16SDJ75Lx27Nih1q1b609/+pMsFouaNm16Uu1AKcJVLRflNMcIZxOuAADAKRYWatNv/xgSsPeuLt4g4eV2u/Xkk09q9uzZ2r17t1wul1wulyIiIo55ns6dO/see4cf7tu3r9KvSUpKkiTt27dPKSkp2rBhgy+sefXs2VPLli2r1Oc60ubNm1VUVKS+ffv6toWGhqpnz55av369JOmWW27RqFGj9NNPP+n888/XyJEj1adPH0nS+PHjdd5556lt27YaOnSoLrroIp1//vkn1RaYCFe1XJTD/BFlu4oC3BIAAFDXWSyWahuaF0hHhqann35azz77rKZPn64zzjhDERERuvPOO1VYWHjM8xxZCMNiscjj8VT6NRaLRZL8XuPd5lXZuWYV8b62onN6tw0bNkzbt2/XZ599piVLlmjQoEGaOHGi/vWvf6l79+7aunWr5s+fryVLlmj06NEaPHiwPvroo5Nu0+mOgha1XBTDAgEAAKrk66+/1ogRI3TNNdeoS5cuatGihTZu3Fjj7Wjbtq1++OEHv22rV68+6fO1atVKdrtd33zzjW9bUVGRVq9erfbt2/u2NWjQQOPHj9c777yj6dOn+xXmiI6O1pgxY/Tf//5Xs2fP1pw5c3To0KGTbtPpLvj/NFHHRXp7rghXAAAAJ6VVq1aaM2eOVqxYobi4OD3zzDNKT0/3CyA14fbbb9eNN96oHj16qE+fPpo9e7bWrl2rFi1aHPe1R1YdlKQOHTrolltu0b333qv4+HilpKRo2rRpysvL04QJEySZ87rOPPNMdezYUS6XS59++qnvcz/77LNKSkpS165dZbVa9eGHHyoxMVGxsbHV+rlPJ4SrWs4358pFuAIAADgZDz30kLZu3aohQ4YoPDxcN910k0aOHKnMzMwabcfVV1+tLVu26J577lFBQYFGjx6t8ePHl+vNqsgVV1xRbtvWrVv15JNPyuPxaOzYscrOzlaPHj20cOFCxcXFSZLsdrsmTZqkbdu2KSwsTOecc45mzZolSYqMjNRTTz2ljRs3ymaz6ayzztLnn38uq5XBbSfLYlRloGcdlZWVpZiYGGVmZio6Ojqgbdl5KE/nTPtCzlCrfn90WEDbAgAA6paCggJt3bpVzZs3l9PpDHRzTkvnnXeeEhMT9fbbbwe6Kae1Y/1bOJFsQM9VLeedc1VQ5FGR26NQG39JAAAACEZ5eXl6+eWXNWTIENlsNr3//vtasmSJFi9eHOimoZoQrmo575wrySxqERdhD2BrAAAAcLIsFos+//xzPfbYY3K5XGrbtq3mzJmjwYMHB7ppqCaEq1ouxGZVWKhN+UVuZROuAAAAglZYWJiWLFkS6GbgFAroGLOvvvpKw4cPV3JysiwWi+bNm3fc17z77rvq0qWLwsPDlZSUpOuuu04HDx70O2bOnDnq0KGDHA6HOnTooLlz556iT1AzvEMDWesKAAAAqL0CGq5yc3PVpUsXvfDCC5U6/ptvvtG1116rCRMm6Ndff9WHH36oVatW6YYbbvAds3LlSo0ZM0Zjx47VmjVrNHbsWI0ePVrff//9qfoYp1ykk3LsAAAAQG0X0GGBw4YN07Bhla+A991336lZs2b6y1/+Iklq3ry5/vznP2vatGm+Y6ZPn67zzjtPkyZNkiRNmjRJy5cv1/Tp0/X+++9X7weoId5y7CwkDAAAANReQVV6rk+fPtq1a5c+//xzGYahvXv36qOPPtKFF17oO2blypU6//zz/V43ZMgQrVix4qjndblcysrK8rvVJlEOhgUCAAAAtV3Qhat3331XY8aMkd1u960g/e9//9t3THp6uhISEvxel5CQoPT09KOed+rUqYqJifHdmjRpcso+w8nwzrmi5woAAACovYIqXP3222/6y1/+oocfflg//vijFixYoK1bt+rmm2/2O85isfg9Nwyj3LayJk2apMzMTN9t586dp6T9J8sbrrIIVwAAAECtFVThaurUqerbt6/uvfdede7cWUOGDNFLL72k119/XWlpaZKkxMTEcr1U+/btK9ebVZbD4VB0dLTfrTaJdJTMuXIRrgAAAE4nAwYM0J133ul73qxZM02fPv2Yr6lsFe7jqa7znE6CKlzl5eXJavVvss1mk2T2TklS7969y61yvWjRIvXp06dmGnkK+EqxFzDnCgAAQDKngtx+++1q0aKFHA6HmjRpouHDh2vp0qWBbpokafjw4UddHHjlypWyWCz66aefTvi8q1at0k033VTV5vmZMmWKunbtWm57WlraCRWfOxkzZ85UbGzsKX2PmhTQaoE5OTnatGmT7/nWrVuVmpqq+Ph4paSkaNKkSdq9e7feeustSeZFeuONN2rGjBkaMmSI0tLSdOedd6pnz55KTk6WJN1xxx3q16+fnnrqKY0YMUL/+9//tGTJEn3zzTcB+YzVIYpS7AAAAD7btm1T3759FRsbq2nTpqlz584qKirSwoULNXHiRP3+++8Vvq6oqEihoaE10sYJEybo0ksv1fbt29W0aVO/fa+//rq6du2q7t27n/B5GzRoUF1NPK7ExMQae6+6IqA9V6tXr1a3bt3UrVs3SdJdd92lbt266eGHH5ZkpuUdO3b4jh8/fryeeeYZvfDCC+rUqZMuv/xytW3bVh9//LHvmD59+mjWrFl644031LlzZ82cOVOzZ89Wr169avbDVSMKWgAAgBphGFJhbmBuJaOQKuPWW2+VxWLRDz/8oMsuu0xt2rRRx44dddddd+m7777zHWexWPTyyy9rxIgRioiI0GOPPSZJmjFjhlq2bCm73a62bdvq7bff9jv/lClTlJKSIofDoeTkZN8yQJL00ksvqXXr1nI6nUpISNBll11WYRsvuugiNWzYUDNnzvTbnpeXp9mzZ2vChAk6ePCgrrzySjVu3Fjh4eE644wzjrt00JHDAjdu3Kh+/frJ6XSqQ4cO5UZwSdJ9992nNm3aKDw8XC1atNBDDz2koiJzRNTMmTP1yCOPaM2aNbJYLLJYLL42HzkscN26dTr33HMVFhamevXq6aabblJOTo5v//jx4zVy5Ej961//UlJSkurVq6eJEyf63utk7NixQyNGjFBkZKSio6M1evRo7d2717d/zZo1GjhwoKKiohQdHa0zzzxTq1evliRt375dw4cPV1xcnCIiItSxY0d9/vnnJ92Wyghoz9WAAQN8w/kqcuTFKEm33367br/99mOe97LLLjvqhR6MvHOu6LkCAACnVFGe9ERyYN77/j2SPeK4hx06dEgLFizQ448/roiI8scfOcRs8uTJmjp1qp599lnZbDbNnTtXd9xxh6ZPn67Bgwfr008/1XXXXafGjRtr4MCB+uijj/Tss89q1qxZ6tixo9LT07VmzRpJZsfAX/7yF7399tvq06ePDh06pK+//rrCdoaEhOjaa6/VzJkz9fDDD/uKq3344YcqLCzU1Vdfrby8PJ155pm67777FB0drc8++0xjx45VixYtKtUx4PF4dOmll6p+/fr67rvvlJWV5Tc/yysqKkozZ85UcnKy1q1bpxtvvFFRUVH629/+pjFjxuiXX37RggULtGTJEklSTExMuXPk5eVp6NChOvvss7Vq1Srt27dPN9xwg2677Ta/39m/+OILJSUl6YsvvtCmTZs0ZswYde3aVTfeeONxP8+RDMPQyJEjFRERoeXLl6u4uFi33nqrxowZoy+//FKSdPXVV6tbt26aMWOGbDabUlNTfb2TEydOVGFhob766itFRETot99+U2Rk5Am340QENFyhcnzDAiloAQAATnObNm2SYRhq165dpY6/6qqrdP311/s9Hz9+vG699VZJ8vV2/etf/9LAgQO1Y8cOJSYmavDgwQoNDVVKSop69uwpyexFiYiI0EUXXaSoqCg1bdrUNwKrItdff73++c9/6ssvv9TAgQMlmUMCL730UsXFxSkuLk733HOP7/jbb79dCxYs0IcfflipcLVkyRKtX79e27ZtU+PGjSVJTzzxRLl5Ug8++KDvcbNmzXT33Xdr9uzZ+tvf/qawsDBFRkYqJCTkmMMA3333XeXn5+utt97yhdoXXnhBw4cP11NPPeUrHhcXF6cXXnhBNptN7dq104UXXqilS5eeVLhasmSJ1q5dq61bt/qWSnr77bfVsWNHrVq1SmeddZZ27Nihe++913c9tG7d2vf6HTt2aNSoUTrjjDMkSS1atDjhNpwowlUQoKAFAACoEaHhZg9SoN67Eryjno61zE5ZPXr08Hu+fv36cgUh+vbtq+eee06SdPnll2v69Olq0aKFhg4dqgsuuEDDhw9XSEiIzjvvPDVt2tS3b+jQobrkkksUHh6ud999V3/+859955w/f77OOecc9enTR6+//roGDhyozZs36+uvv9aiRYskSW63W08++aRmz56t3bt3y+VyyeVyVdgjV5H169crJSXFF6wks7jbkT766CNNnz5dmzZtUk5OjoqLi0+4Ovb69evVpUsXv7b17dtXHo9HGzZs8IWrjh07+grOSVJSUpLWrVt3Qu9V9j2bNGnitwZthw4dFBsbq/Xr1+uss87SXXfdpRtuuEFvv/22Bg8erMsvv1wtW7aUJP3lL3/RLbfcokWLFmnw4MEaNWqUOnfufFJtqaygqhZ4uvLNuaLnCgAAnEoWizk0LxC3Soal1q1by2KxaP369ZU6vqKgcqw1UZs0aaINGzboxRdfVFhYmG699Vb169dPRUVFioqK0k8//aT3339fSUlJevjhh9WlSxdlZGTo4osvVmpqqu/mDXUTJkzQnDlzlJWVpTfeeENNmzbVoEGDJElPP/20nn32Wf3tb3/TsmXLlJqaqiFDhqiwsLBSn62i6TVHfrbvvvtOV1xxhYYNG6ZPP/1UP//8sx544IFKv0dF39Gx3vPIgiEWi0Uej+eE3ut471l2+5QpU/Trr7/qwgsv1LJly9ShQwfNnTtXknTDDTdoy5YtGjt2rNatW6cePXro3//+90m1pbIIV0Egylk65+pYc9QAAADquvj4eA0ZMkQvvviicnNzy+3PyMg45uvbt29fror0ihUr1L59e9/zsLAwXXzxxXr++ef15ZdfauXKlb7el5CQEA0ePFjTpk3T2rVrtW3bNi1btkxRUVFq1aqV7xYWFiZJGj16tGw2m9577z29+eabuu6663zB4Ouvv9aIESN0zTXXqEuXLmrRooU2btxY6e+iQ4cO2rFjh/bsKe1tXLlypd8x3377rZo2baoHHnhAPXr0UOvWrbV9+3a/Y+x2u9xu93HfKzU11e87//bbb2W1WtWmTZtKt/lEeD/fzp07fdt+++03ZWZm+v282rRpo7/+9a9atGiRLr30Ur3xxhu+fU2aNNHNN9+sjz/+WHfffbf++9//npK2ejEsMAhEOswfk9tjKL/IrXA7PzYAAHD6eumll9SnTx/17NlT//jHP9S5c2cVFxdr8eLFmjFjxjF7te69916NHj1a3bt316BBg/R///d/+vjjj33FHGbOnCm3261evXopPDxcb7/9tsLCwtS0aVN9+umn2rJli/r166e4uDh9/vnn8ng8atu27VHfLzIyUmPGjNH999+vzMxMjR8/3revVatWmjNnjlasWKG4uDg988wzSk9P9wsOxzJ48GC1bdtW1157rZ5++mllZWXpgQce8DumVatW2rFjh2bNmqWzzjpLn332ma9nx6tZs2a+JZEaN26sqKgoORwOv2OuvvpqTZ48WePGjdOUKVO0f/9+3X777Ro7dqxvSODJcrvdSk1N9dtmt9s1ePBgde7cWVdffbWmT5/uK2jRv39/9ejRQ/n5+br33nt12WWXqXnz5tq1a5dWrVqlUaNGSZLuvPNODRs2TG3atNHhw4e1bNmySn+3J4ueqyAQbrfJWtIjSjl2AABwumvevLl++uknDRw4UHfffbc6deqk8847T0uXLtWMGTOO+dqRI0fqueee0z//+U917NhR//nPf/TGG29owIABksxqg//973/Vt29fde7cWUuXLtX//d//qV69eoqNjdXHH3+sc889V+3bt9fLL7+s999/Xx07djzme06YMEGHDx/W4MGDlZKS4tv+0EMPqXv37hoyZIgGDBigxMREjRw5stLfg9Vq1dy5c+VyudSzZ0/dcMMNevzxx/2OGTFihP7617/qtttuU9euXbVixQo99NBDfseMGjVKQ4cO1cCBA9WgQYMKy8GHh4dr4cKF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yjAMuVwurh0c06BBg7Ru3Tqlpqb6bj169NDVV1+t1NRUtWjRgusHleZyubR+/XolJSUF7X97GBZYA+666y6NHTtWPXr0UO/evfXKK69ox44duvnmmwPdNNQyOTk52rRpk+/51q1blZqaqvj4eKWkpOjOO+/UE088odatW6t169Z64oknFB4erquuuiqArUZtMHHiRL333nv63//+p6ioKN9f+mJiYhQWFuZbd4brBxW5//77NWzYMDVp0kTZ2dmaNWuWvvzySy1YsIBrB8cUFRXlm9vpFRERoXr16vm2c/3gaO655x4NHz5cKSkp2rdvnx577DFlZWVp3LhxwfvfnoDVKTzNvPjii0bTpk0Nu91udO/e3VceGSjriy++MCSVu40bN84wDLMs6eTJk43ExETD4XAY/fr1M9atWxfYRqNWqOi6kWS88cYbvmO4fnA0119/ve//UQ0aNDAGDRpkLFq0yLefawcnomwpdsPg+sHRjRkzxkhKSjJCQ0ON5ORk49JLLzV+/fVX3/5gvHYshmEYAcp1AAAAAFBnMOcKAAAAAKoB4QoAAAAAqgHhCgAAAACqAeEKAAAAAKoB4QoAAAAAqgHhCgAAAACqAeEKAAAAAKoB4QoAAAAAqgHhCgCAamaxWDRv3rxANwMAUMMIVwCAOmX8+PGyWCzlbkOHDg100wAAdVxIoBsAAEB1Gzp0qN544w2/bQ6HI0CtAQCcLui5AgDUOQ6HQ4mJiX63uLg4SeaQvRkzZmjYsGEKCwtT8+bN9eGHH/q9ft26dTr33HMVFhamevXq6aabblJOTo7fMa+//ro6duwoh8OhpKQk3XbbbX77Dxw4oEsuuUTh4eFq3bq1Pvnkk1P7oQEAAUe4AgCcdh566CGNGjVKa9as0TXXXKMrr7xS69evlyTl5eVp6NChiouL06pVq/Thhx9qyZIlfuFpxowZmjhxom666SatW7dOn3zyiVq1auX3Ho888ohGjx6ttWvX6oILLtDVV1+tQ4cO1ejnBADULIthGEagGwEAQHUZP3683nnnHTmdTr/t9913nx566CFZLBbdfPPNmjFjhm/f2Wefre7du+ull17Sf//7X913333auXOnIiIiJEmff/65hg8frj179ighIUGNGjXSddddp8cee6zCNlgsFj344IN69NFHJUm5ubmKiorS559/ztwvAKjDmHMFAKhzBg4c6BeeJCk+Pt73uHfv3n77evfurdTUVEnS+vXr1aVLF1+wkqS+ffvK4/Fow4YNslgs2rNnjwYNGnTMNnTu3Nn3OCIiQlFRUdq3b9/JfiQAQBAgXAEA6pyIiIhyw/SOx2KxSJIMw/A9ruiYsLCwSp0vNDS03Gs9Hs8JtQkAEFyYcwUAOO1899135Z63a9dOktShQwelpqYqNzfXt//bb7+V1WpVmzZtFBUVpWbNmmnp0qU12mYAQO1HzxUAoM5xuVxKT0/32xYSEqL69etLkj788EP16NFDf/rTn/Tuu+/qhx9+0GuvvSZJuvrqqzV58mSNGzdOU6ZM0f79+3X77bdr7NixSkhIkCRNmTJFN998sxo2bKhhw4YpOztb3377rW6//faa/aAAgFqFcAUAqHMWLFigpKQkv21t27bV77//Lsms5Ddr1izdeuutSkxM1LvvvqsOHTpIksLDw7Vw4ULdcccdOuussxQeHq5Ro0bpmWee8Z1r3LhxKigo0LPPPqt77rlH9evX12WXXVZzHxAAUCtRLRAAcFqxWCyaO3euRo4cGeimAADqGOZcAQAAAEA1IFwBAAAAQDVgzhUA4LTCaHgAwKlCzxUAAAAAVAPCFQAAAABUA8IVAAAAAFQDwhUAAAAAVAPCFQAAAABUA8IVAAAAAFQDwhUAAAAAVAPCFQAAAABUg/8HvJ+jrFJTraIAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Calculate and print accuracies for training and cross-validation sets\n",
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    # Training set accuracy\n",
    "    tr_correct = 0\n",
    "    tr_total = 0\n",
    "    for images, labels in trLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        tr_total += labels.size(0)\n",
    "        tr_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    tr_accuracy = 100 * tr_correct / tr_total\n",
    "    \n",
    "    # Cross-validation set accuracy\n",
    "    cv_correct = 0\n",
    "    cv_total = 0\n",
    "    for images, labels in cvLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        cv_total += labels.size(0)\n",
    "        cv_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    cv_accuracy = 100 * cv_correct / cv_total\n",
    "\n",
    "print(f'Accuracy on training set: {tr_accuracy:.2f}%')\n",
    "print(f'Accuracy on cross-validation set: {cv_accuracy:.2f}%')\n",
    "\n",
    "# Plot training and cross-validation losses\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.plot(range(1, num_epochs+1), train_losses, label='Training Loss')\n",
    "plt.plot(range(1, num_epochs+1), cv_losses, label='Cross-Validation Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Training and Cross-Validation Loss')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "authorship_tag": "ABX9TyO5gS9/MePw+FDiXJA07L6y",
   "include_colab_link": true,
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
